#include "ether_frame.h"
#include "extract.h"
#include "ethertype.h"
#include "public.h"
#include "ip_packet.h"

const struct tok ethertype_values[] = {
{ ETHERTYPE_IP,		"IPv4" },
{ ETHERTYPE_MPLS,		"MPLS unicast" },
{ ETHERTYPE_MPLS_MULTI,	"MPLS multicast" },
{ ETHERTYPE_IPV6,		"IPv6" },
{ ETHERTYPE_8021Q,		"802.1Q" },
{ ETHERTYPE_8021Q9100,	"802.1Q-9100" },
{ ETHERTYPE_8021QinQ,	"802.1Q-QinQ" },
{ ETHERTYPE_8021Q9200,	"802.1Q-9200" },
{ ETHERTYPE_VMAN,		"VMAN" },
{ ETHERTYPE_PUP,            "PUP" },
{ ETHERTYPE_ARP,            "ARP"},
{ ETHERTYPE_REVARP,         "Reverse ARP"},
{ ETHERTYPE_NS,             "NS" },
{ ETHERTYPE_SPRITE,         "Sprite" },
{ ETHERTYPE_TRAIL,          "Trail" },
{ ETHERTYPE_MOPDL,          "MOP DL" },
{ ETHERTYPE_MOPRC,          "MOP RC" },
{ ETHERTYPE_DN,             "DN" },
{ ETHERTYPE_LAT,            "LAT" },
{ ETHERTYPE_SCA,            "SCA" },
{ ETHERTYPE_TEB,            "TEB" },
{ ETHERTYPE_LANBRIDGE,      "Lanbridge" },
{ ETHERTYPE_DECDNS,         "DEC DNS" },
{ ETHERTYPE_DECDTS,         "DEC DTS" },
{ ETHERTYPE_VEXP,           "VEXP" },
{ ETHERTYPE_VPROD,          "VPROD" },
{ ETHERTYPE_ATALK,          "Appletalk" },
{ ETHERTYPE_AARP,           "Appletalk ARP" },
{ ETHERTYPE_IPX,            "IPX" },
{ ETHERTYPE_PPP,            "PPP" },
{ ETHERTYPE_MPCP,           "MPCP" },
{ ETHERTYPE_SLOW,           "Slow Protocols" },
{ ETHERTYPE_PPPOED,         "PPPoE D" },
{ ETHERTYPE_PPPOES,         "PPPoE S" },
{ ETHERTYPE_EAPOL,          "EAPOL" },
{ ETHERTYPE_RRCP,           "RRCP" },
{ ETHERTYPE_MS_NLB_HB,      "MS NLB heartbeat" },
{ ETHERTYPE_JUMBO,          "Jumbo" },
{ ETHERTYPE_LOOPBACK,       "Loopback" },
{ ETHERTYPE_ISO,            "OSI" },
{ ETHERTYPE_GRE_ISO,        "GRE-OSI" },
{ ETHERTYPE_CFM_OLD,        "CFM (old)" },
{ ETHERTYPE_CFM,            "CFM" },
{ ETHERTYPE_IEEE1905_1,     "IEEE1905.1" },
{ ETHERTYPE_LLDP,           "LLDP" },
{ ETHERTYPE_TIPC,           "TIPC"},
{ ETHERTYPE_GEONET_OLD,     "GeoNet (old)"},
{ ETHERTYPE_GEONET,         "GeoNet"},
{ ETHERTYPE_CALM_FAST,      "CALM FAST"},
{ ETHERTYPE_AOE,            "AoE" },
{ 0, NULL}
};

EtherFrame::EtherFrame(const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
    ep = (struct ether_header *)packet;
    length = pkthdr->len;
    caplen = pkthdr->caplen;
    ts = pkthdr->ts;
}

int EtherFrame::ip_dissection(u_char *nxtLayer, uint length)
{
    IPPacket ippacket(nxtLayer, length);
    ippacket.dissection();
    return 0;
}

//return length after dissection
int EtherFrame::dealNexLayer(u_short ether_type, u_char *nxtLayer, uint nxtLayerLength, uint nxtCaplen)
{
    switch (ether_type) {

    case ETHERTYPE_IP:
        ip_dissection(nxtLayer, nxtLayerLength);
        return 0;

    case ETHERTYPE_IPV6:

    case ETHERTYPE_ARP:
    case ETHERTYPE_REVARP:

    case ETHERTYPE_DN:

    case ETHERTYPE_ATALK:

    case ETHERTYPE_AARP:

    case ETHERTYPE_IPX:

    case ETHERTYPE_ISO:


    case ETHERTYPE_PPPOED:
    case ETHERTYPE_PPPOES:
    case ETHERTYPE_PPPOED2:
    case ETHERTYPE_PPPOES2:

    case ETHERTYPE_EAPOL:

    case ETHERTYPE_RRCP:

    case ETHERTYPE_PPP:

    case ETHERTYPE_MPCP:

    case ETHERTYPE_SLOW:

    case ETHERTYPE_CFM:
    case ETHERTYPE_CFM_OLD:

    case ETHERTYPE_LLDP:

    case ETHERTYPE_LOOPBACK:

    case ETHERTYPE_MPLS:
    case ETHERTYPE_MPLS_MULTI:

    case ETHERTYPE_TIPC:

    case ETHERTYPE_MS_NLB_HB:

    case ETHERTYPE_GEONET_OLD:
    case ETHERTYPE_GEONET:

    case ETHERTYPE_CALM_FAST:

    case ETHERTYPE_AOE:
        printf("ether_type:%s not implemented\n", tok2str(ethertype_values,"0x%04x", ether_type));
        return 0;

    case ETHERTYPE_LAT:
    case ETHERTYPE_SCA:
    case ETHERTYPE_MOPRC:
    case ETHERTYPE_MOPDL:
    case ETHERTYPE_IEEE1905_1:
        /* default_print for now */
    default:
        printf("ether_type:%s error\n", tok2str(ethertype_values,"0x%04x", ether_type));
        return -1;
    }
}

int EtherFrame::dissection()
{
    u_short ether_type;
    uint nxtLayerLength = 0, nxtCaplen = 0;
    u_char *nxtLayer;
    int ret = -1;

    if (caplen < ETHER_HDRLEN || length < ETHER_HDRLEN) {
        printf("[|ether]\n");
        return 0;
    }

    nxtLayerLength = length - ETHER_HDRLEN;
    nxtCaplen = caplen -  ETHER_HDRLEN;
    nxtLayer = (u_char *)ep + ETHER_HDRLEN;

    ether_type = EXTRACT_16BITS(&ep->ether_type);

    while (nxtCaplen > 0)
    {
        /*
         * Is it (gag) an 802.3 encapsulation?
         */
        if (ether_type <= ETHERMTU) {
            /* Try to print the LLC-layer header & higher layers */
            printf("llc parse have not implemented\n");
            nxtCaplen = 0;
        } else if (ether_type == ETHERTYPE_8021Q  ||
                   ether_type == ETHERTYPE_8021Q9100 ||
                   ether_type == ETHERTYPE_8021Q9200 ||
                   ether_type == ETHERTYPE_8021QinQ) {
            /*
             * Print VLAN information, and then go back and process
             * the enclosed type field.
             */
            if (nxtCaplen < 4 || nxtLayerLength < 4) {
                printf("[|vlan]");
                return 0;
            }

            ether_type = EXTRACT_16BITS(nxtLayer + 2);
            if (ether_type > ETHERMTU)
                printf("ethertype %s, ", tok2str(ethertype_values,"0x%04x", ether_type));
            nxtLayer += 4;
            nxtLayerLength -= 4;
            nxtCaplen -= 4;
        } else if (ether_type == ETHERTYPE_JUMBO) {
            /*
             * Alteon jumbo frames.
             * See
             *
             *	http://tools.ietf.org/html/draft-ietf-isis-ext-eth-01
             *
             * which indicates that, following the type field,
             * there's an LLC header and payload.
             */
            /* Try to print the LLC-layer header & higher layers */
            printf("JUMBO have not implemented\n");
            nxtCaplen = 0;
        } else {
            if ((ret = dealNexLayer(ether_type, nxtLayer, nxtLayerLength, nxtCaplen)) < 0) {
                /* ether_type not known, print raw packet */
                printf("ether type unknown\n");
                nxtCaplen = 0;
            }
            else
            {
                nxtCaplen = ret;
            }
        }
    }
    return 0;
}
